﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TUtils.Multithreading;
using System.Threading;
using TUtils.Threads;

namespace MMessaging.MessagingEx.Common
{
	/// <summary>
	/// Repräsentiert einen Thread aus einer Menge von Threads, die 
	/// niemals gleichzeitig aktiv sind. 
	/// </summary>
	internal interface IMThread
	{
		/// <summary>
		/// Wird von einem anderen Thread aus aufgerufen, um den Thread aus einem Wartezustand zu wecken.
		/// Das Setzen dieses Signals (Signal.Set()) weckt den Thread aus dem Wartezustand.
		/// </summary>
		ManualResetEvent Signal { get; }

		/// <summary>
		/// Diese Methode übergibt die Kontrolle an einen anderen Thread.
		/// Wird von diesem Thread aus aufgerufen.
		/// Bewirkt, dass dieser Thread in den Wartezustand versetzt wird und erst wieder aufwacht, wenn entweder
		/// das Signal gesetzt wird (Signal.Set()) oder der Thread anderweitig beendet wird.
		/// </summary>
		/// <param name="newController">
		/// der Thread, der nun aktiv sein soll.
		/// </param>
		void GiveControlAwayAndWait(IMThread newController);
	}

	/// <summary>
	/// Repräsentiert einen Thread aus einer Menge von Threads, die 
	/// niemals gleichzeitig aktiv sind. 
	/// </summary>
	internal class MThread<THREADCLASS> : TThreadX<THREADCLASS>, IMThread
		where THREADCLASS : MThread<THREADCLASS>
	{
		#region private / protected

		private ManualResetEvent _Signal;

		/// <summary>
		/// Die blockierende Methode darf nur von diesem Thread aus aufgerufen werden und
		/// blockiert solange, bis der Thread wieder mittels Signal.Set aufgeweckt wird oder 
		/// mittels CancelAsync() gestoppt wird.
		/// </summary>
		protected void WaitForSignal()
		{
			while (!IsCanceling)
			{
				if (Signal.WaitOne(MMessagingHelper.CANCEL_WAIT_INTERVALL))
					break;
			}
		}

		#endregion

		#region public

		/// <summary>
		/// Wird von einem anderen Thread aus aufgerufen, um den Thread aus einem Wartezustand zu wecken.
		/// Das Setzen dieses Signals (Signal.Set()) weckt den Thread aus dem Wartezustand.
		/// </summary>
		public ManualResetEvent Signal
		{
			get
			{
				if (_Signal == null)
					_Signal = new ManualResetEvent(false);
				return _Signal;
			}
		}

		/// <summary>
		/// Diese Methode übergibt die Kontrolle an einen anderen Thread.
		/// Wird von diesem Thread aus aufgerufen.
		/// Bewirkt, dass dieser Thread in den Wartezustand versetzt wird und erst wieder aufwacht, wenn entweder
		/// das Signal.Set() oder CancelAsync() aufgerufen wird.
		/// </summary>
		/// <param name="newController">
		/// der Thread, der nun aktiv sein soll.
		/// </param>
		public void GiveControlAwayAndWait(IMThread newController)
		{
			Signal.Reset();
			newController.Signal.Set();
			WaitForSignal();
		}

		#endregion

		#region constructor

		/// <summary>
		/// Nicht vergessen Init(..) und StartAsync() anschließend aufzurufen !
		/// </summary>
		public MThread()
		{
			Signal.Reset();
		}

		#endregion
	}
}
